package com.baixun.gassystem.service.impl;

import com.baixun.gascommon.config.ApiConfig;
import com.baixun.gascommon.enums.ResultStatus;
import com.baixun.gascommon.exception.CustomException;
import com.baixun.gascommon.utils.*;
import com.baixun.gasentity.dto.LoginDto;
import com.baixun.gasentity.entity.SysDepartmentRolePermissions;
import com.baixun.gasentity.entity.SysDepartmentUserRoles;
import com.baixun.gasentity.entity.SysUser;
import com.baixun.gassystem.service.*;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baixun.gassystem.mapper.SysUserMapper;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.ObjectMapper;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.core.authority.SimpleGrantedAuthority;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.stereotype.Service;
import org.springframework.util.DigestUtils;

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.net.HttpURLConnection;
import java.net.SocketTimeoutException;
import java.net.URL;
import java.nio.charset.StandardCharsets;
import java.util.*;
import java.util.concurrent.TimeUnit;
import java.util.stream.Collectors;

/**
* @author 智少
* @description 针对表【sys_user】的数据库操作Service实现
* @createDate 2024-09-26 17:42:54
*/

@Service
public class SysUserServiceImpl extends ServiceImpl<SysUserMapper, SysUser>
    implements SysUserService{

    @Autowired
    public ObjectMapper objectMapper;

    @Autowired
    public SysAuthorityService sysAuthorityService;

    @Value("${redis.name}")
    public String tokenName;

    @Autowired
    private RedisUtils redisUtils;

    @Autowired
    private TokenUntil tokenUntil;

    @Autowired
    private SysDepartmentUserRolesService sysDepartmentUserRolesService;

    @Autowired
    private SysDepartmentRolePermissionsService sysDepartmentRolePermissionsService;

    @Autowired
    private ApiService apiService;





    @Override
    public Map<String, Object> evslogin(LoginDto loginDto) throws Exception {
        String loginUrl = ApiConfig.DOMAIN + ApiConfig.LOGIN_API;

        // 构建 Authorization 验证字符串
        String base64String = loginDto.getUserName() + ApiConfig.API_SECRET + loginDto.getPassWord();
        String encodedAuth = Base64.getEncoder().encodeToString(base64String.getBytes(StandardCharsets.UTF_8));

        // 请求头信息
        HashMap<String, String> headers = new HashMap<>();
        headers.put("Authorization", encodedAuth);
        headers.put("Content-Type", "application/json; charset=UTF-8");

        // 构建登录请求体
        String loginPayload = String.format(
                "{\"username\": \"%s\", \"password\": \"%s\", \"verificationCode\": \"%s\", \"verificationCodeToken\": \"%s\"}",
                loginDto.getUserName(), loginDto.getPassWord(), loginDto.getVerificationCode(), loginDto.getVerificationCodeToken()
        );

        HttpURLConnection connection = null;
        try {
            URL url = new URL(loginUrl);
            connection = (HttpURLConnection) url.openConnection();
            connection.setRequestMethod("POST");
            connection.setDoOutput(true);
            connection.setConnectTimeout(10000); // 设置连接超时
            connection.setReadTimeout(10000);    // 设置读取超时

            headers.forEach(connection::setRequestProperty);
            connection.getOutputStream().write(loginPayload.getBytes(StandardCharsets.UTF_8));

            int responseCode = connection.getResponseCode();
            BufferedReader reader;

            if (responseCode == HttpURLConnection.HTTP_OK) {
                reader = new BufferedReader(new InputStreamReader(connection.getInputStream(), StandardCharsets.UTF_8));
            } else {
                reader = new BufferedReader(new InputStreamReader(connection.getErrorStream(), StandardCharsets.UTF_8));
            }

            StringBuilder responseBuilder = new StringBuilder();
            String line;
            while ((line = reader.readLine()) != null) {
                responseBuilder.append(line);
            }
            reader.close();

            // 将响应体转换为Map
            Map<String, Object> responseBody = objectMapper.readValue(responseBuilder.toString(), Map.class);

            // 成功响应判断
            if (responseBody.containsKey("userIdForCurrentLogin") && responseBody.containsKey("redirect")) {
                // 提取并缓存用户的 SAAS_SESSION token
                String userToken = parseTokenFromHeaders(connection.getHeaderFields());
                if (userToken != null) {
                    // 设置过期时间为半小时（1800秒）
                    long tokenExpiryTime = TimeUnit.MINUTES.toSeconds(30);

                    // 存储或刷新用户的 token
                    String userTokenKey = "userToken:" + loginDto.getUserName();
                    redisUtils.setWithExpire(userTokenKey, userToken, tokenExpiryTime);
                } else {
                    throw new CustomException("用户登录成功，但未能提取有效的 token", 500);
                }

                return responseBody; // 成功响应

            } else if (responseBody.containsKey("code") && responseBody.containsKey("message")) {
                // 失败响应判断
                throw new CustomException("EVS 登录失败：Code = " + responseBody.get("code") + ", Message = " + responseBody.get("message"), responseCode);
            } else {
                throw new CustomException("未知的 EVS 响应格式：" + responseBuilder.toString(), 500);
            }
        } catch (SocketTimeoutException e) {
            throw new CustomException("网络超时，请检查网络连接或稍后重试", 504);
        } catch (IOException e) {
            throw new CustomException("IO错误，请检查网络或稍后重试", 500);
        } finally {
            if (connection != null) {
                connection.disconnect();
            }
        }
    }


    @Override
    /**
     * 登录方法
     *
     * @param loginDto 登录数据传输对象，包含登录所需的信息
     * @return 返回一个R对象，其中包含登录结果和可能的令牌
     * @throws JsonProcessingException 如果对象转换为JSON字符串时发生错误
     */
    public R  Login(LoginDto loginDto) throws Exception {
        Map<String, Object> userIdMap = evslogin(loginDto);
        //System.out.println("前端数据"+userIdMap);
        Long userId = ((Number) userIdMap.get("userIdForCurrentLogin")).longValue();

        if (userId != null) {
            // 缓存 userId
            String userIdKey = "userId";
            redisUtils.set(userIdKey, userId.toString(), TimeUnit.DAYS.toSeconds(5));

            // 获取并缓存用户部门权限列表 allowedIds
            List<Long> allowedIds = sysDepartmentUserRolesService.selectDeptIdsByUserId(userId);
            String permissionsKey = "permissions:" + userId;
            redisUtils.setList(permissionsKey, allowedIds, TimeUnit.DAYS.toSeconds(5));

            // 查询用户角色和权限
            LambdaQueryWrapper<SysDepartmentUserRoles> userRoleQuery = new LambdaQueryWrapper<>();
            userRoleQuery.eq(SysDepartmentUserRoles::getUserId, userId);
            List<SysDepartmentUserRoles> userRoles = sysDepartmentUserRolesService.list(userRoleQuery);

            List<SimpleGrantedAuthority> grantedAuthorities = new ArrayList<>();
            for (SysDepartmentUserRoles userRole : userRoles) {
                Long roleId = userRole.getRoleId();

                LambdaQueryWrapper<SysDepartmentRolePermissions> rolePermissionQuery = new LambdaQueryWrapper<>();
                rolePermissionQuery.eq(SysDepartmentRolePermissions::getRoleId, roleId);
                List<SysDepartmentRolePermissions> rolePermissions = sysDepartmentRolePermissionsService.list(rolePermissionQuery);

                List<SimpleGrantedAuthority> roleAuthorities = rolePermissions.stream()
                        .map(rolePermission -> new SimpleGrantedAuthority(rolePermission.getPermissionId().toString()))
                        .collect(Collectors.toList());

                grantedAuthorities.addAll(roleAuthorities);
            }

            // 创建认证 token 并设置权限
            UsernamePasswordAuthenticationToken authenticationToken =
                    new UsernamePasswordAuthenticationToken(userId, null, grantedAuthorities);
            SecurityContextHolder.getContext().setAuthentication(authenticationToken);

            // 生成 JWT token
            String token = tokenUntil.createToken(String.valueOf(userId), grantedAuthorities.stream()
                    .map(SimpleGrantedAuthority::getAuthority)
                    .collect(Collectors.toList()));

            // 将 token 存储到 Redis
            String tokenKey = "token:" + userId;
            redisUtils.set(tokenKey, token, TimeUnit.DAYS.toSeconds(5));

            return R.success(token);
        } else {
            throw new Exception("登录失败，未能获取有效的 userId");
        }
    }

    // 获取验证码的方法
    @Override
    public R<Map<String, Object>> getVerificationCode() {
        String verificationUrl = ApiConfig.DOMAIN + "/api/v2/verificationCode";
        HttpURLConnection connection = null;

        try {
            URL url = new URL(verificationUrl);
            connection = (HttpURLConnection) url.openConnection();
            connection.setRequestMethod("GET");
            connection.setConnectTimeout(10000);  // 连接超时
            connection.setReadTimeout(10000);     // 读取超时

            int responseCode = connection.getResponseCode();
            if (responseCode == HttpURLConnection.HTTP_OK) {
                BufferedReader reader = new BufferedReader(new InputStreamReader(connection.getInputStream(), StandardCharsets.UTF_8));
                StringBuilder responseBuilder = new StringBuilder();
                String line;
                while ((line = reader.readLine()) != null) {
                    responseBuilder.append(line);
                }
                reader.close();

                // 将响应转换为 Map
                Map<String, Object> responseBody = objectMapper.readValue(responseBuilder.toString(), Map.class);

                // 直接返回验证码接口的响应内容
                return R.success(responseBody);
            } else {
                throw new CustomException("获取验证码失败，响应码：" + responseCode, responseCode);
            }
        } catch (SocketTimeoutException e) {
            throw new CustomException("网络超时，请检查网络连接或稍后重试", 504);
        } catch (IOException e) {
            throw new CustomException("IO错误，请检查网络或稍后重试", 500);
        } finally {
            if (connection != null) {
                connection.disconnect();
            }
        }
    }



//
//    /**
//     * 加载用户权限数据，并存储到 Redis
//     * @param userId 用户ID
//     */
//    public void loadUserPermissions(Long userId) {
//        // 查询用户的所有部门权限ID
//        List<UserOrderDto> allowedDeptIds = sysDepartmentUserRolesService.selectDepartmentAll(userId);
//
//        // 将权限数据存储到 Redis，设置一个过期时间（如5天）
//        String permissionsKey = "permissions:" + userId;
//        redisUtils.set(permissionsKey, allowedDeptIds, TimeUnit.DAYS.toSeconds(5));
//    }

    @Override
    public R info() {
        SysUser sysUser = this.getById(ThreadLocalUtils.getThreadLocal());
        return R.success(sysUser);
    }
    /**
     * 从响应头中解析SAAS_SESSION token
     */
    private String parseTokenFromHeaders(Map<String, List<String>> headerFields) {
        for (Map.Entry<String, List<String>> entry : headerFields.entrySet()) {
            for (String value : entry.getValue()) {
                if (value.startsWith("SAAS_SESSION=")) {
                    return value.split(";")[0].split("=")[1];
                }
            }
        }
        return null;
    }

    /**
     * 缓存管理员token，如果管理员token不存在或即将过期
     */
    private void cacheAdminTokenIfNeeded() throws Exception {
        String adminToken = redisUtils.get("adminToken");
        if (adminToken == null || isTokenExpiringSoon("adminToken")) {
            adminToken = apiService.getAuthToken();  // 调用管理员登录方法获取新的token
            if (adminToken != null) {
                redisUtils.set("adminToken", adminToken, TimeUnit.MINUTES.toSeconds(30));
            } else {
                throw new Exception("管理员登录失败，未能获取有效的token");
            }
        }
    }

    /**
     * 判断token是否即将过期
     */
    private boolean isTokenExpiringSoon(String redisKey) {
        Long expireTime = redisUtils.getExpire(redisKey);
        return expireTime != null && expireTime <= TimeUnit.MINUTES.toSeconds(10);
    }

    @Override
    /**
     * 用户登出方法
     *
     * 该方法用于处理用户登出操作，通过检查Redis中是否存在当前用户的令牌来验证用户是否已登录
     * 如果令牌存在，则从Redis中删除该令牌，表示用户已成功登出
     * 如果令牌不存在，则返回失败信息，表示用户未登录或令牌已失效
     *
     * @return 返回结果对象R，包含操作状态和提示信息
     */

    public R logout() {
        // 检查Redis中是否存在当前用户的令牌
        if (redisUtils.get(tokenName + ThreadLocalUtils.getThreadLocal()) == null) {
            // 如果令牌不存在，返回失败结果，状态为TokenNotExist
            return R.fail(ResultStatus.TokenNotExist);
        }
        // 从Redis中删除当前用户的令牌，用户成功登出
        redisUtils.remove(tokenName + ThreadLocalUtils.getThreadLocal());
        // 返回成功结果，提示用户退出成功
        return R.success("退出成功");
    }

    /**
     * 重置密码查找功能方法
     * 该方法根据登录名查找用户，并返回用户的密码
     *
     * @param loginDto 包含用户登录名的登录数据传输对象
     * @return 返回包含用户密码的结果对象，如果找不到用户则返回失败信息
     */
//    @Override
//    public R findPassword(LoginDto loginDto) {
//        // 创建一个查询构造器，用于精确查询用户名
//        LambdaQueryWrapper<SysUser> sysUserLambdaQueryWrapper = new LambdaQueryWrapper<>();
//        sysUserLambdaQueryWrapper.eq(SysUser::getUserName, loginDto.getUserName());
//        // 限制查询结果返回数量，只返回一条
//        sysUserLambdaQueryWrapper.last("limit 1");
//
//        // 执行查询，获取单个用户对象
//        SysUser one = this.getOne(sysUserLambdaQueryWrapper);
//
//        // 判断是否查询到用户
//        if (one == null) {
//            // 如果用户不存在，返回失败结果
//            return R.fail(ResultStatus.NoUserFind);
//        }
//
//        // 如果用户存在，返回用户的密码
//        return R.success(one.getPassWord());
//    }

    /**
     * 重写Md5Password方法，对输入的密码进行MD5加密处理
     *
     * @param password 待加密的密码
     * @return 返回加密后的密码字符串
     */
    @Override
    public R Md5Password(String password) {
        // 使用MD5算法将密码转换为16进制字符串形式，以隐藏原始密码信息
        String s = DigestUtils.md5DigestAsHex(password.getBytes());
        // 返回一个成功的结果对象，将加密后的密码作为结果内容
        return R.success(s);
    }
    /**
     * 重写删除方法，使用线程本地变量进行删除操作
     *
     * @return 删除成功时返回成功信息封装在R对象中
     */
    @Override
    public R delete() {
        // 获取线程本地变量中的ID，该ID用于标识哪个线程发起的删除操作
        Long threadLocal = ThreadLocalUtils.getThreadLocal();
        // 使用获取的线程本地变量ID调用removeById方法进行删除操作
        boolean b = this.removeById(threadLocal);
        // 返回删除成功的消息封装在R对象中
        return R.success("删除成功");
    }
}




